package org.dslintegration.merge.core;

import java.util.ArrayList;
import java.util.List;

import org.dslintegration.history.EMetamodelSource;
import org.dslintegration.history.MergeAction;
import org.dslintegration.history.MergeHistory;
import org.dslintegration.merge.km3.core.Class;
import org.dslintegration.merge.km3.core.Classifier;
import org.dslintegration.merge.km3.core.DataType;
import org.dslintegration.merge.km3.core.Metamodel;
import org.dslintegration.merge.km3.core.ModelElement;
import org.dslintegration.merge.km3.core.Package;
import org.dslintegration.merge.km3.core.StructuralFeature;

public class MergeContext {

	public List<Classifier> classifiers = new ArrayList<Classifier>();
	public List<StructuralFeature> features = new ArrayList<StructuralFeature>();
	public List<Package> packages = new ArrayList<Package>();
	public Metamodel metamodel;
	public MergeHistory history = new MergeHistory();

	public MergeContext(Metamodel metamodel) {
		this.metamodel = metamodel;
		packages.addAll(metamodel.getContents());
		for (Package p : metamodel.getContents()) {
			for (ModelElement me : p.getContents()) {
				if (me instanceof Classifier) {
					if (me instanceof DataType)
						classifiers.add((Classifier) me);
					if(me instanceof Class)
						classifiers.add((Classifier) me);
				}

				if (me instanceof Class) {
					for (StructuralFeature feature : ((Class) me).getStructuralFeatures()) {
						
						features.add(feature);
					}
				}
			}
		}
	}

	public void changeType(Classifier oldc, Classifier newc, boolean deleteOld) {
		//For attributes
		for (StructuralFeature f : features) {
			if (f.getType() == oldc) {
				f.setType(newc);
			}
			/*
			if(f instanceof Reference){
				Reference ref = (Reference) f;
				if(ref.getOpposite()!=null){
					if(ref.getOpposite().getParent() == oldc);
				}
			}*/
		}
		//For supertypes
		for (Classifier classifier : classifiers) {
			if (classifier instanceof Class) {
				Class clazz = (Class) classifier;
			if(clazz.getSuperTypeRef()!=null && clazz.getSuperTypeRef() == oldc){
					clazz.setSuperType(newc.getName());
					clazz.setSuperTypeRef((Class) newc);
				}
			}
		}

		history.add(MergeAction.getChange(oldc, newc));
		if (deleteOld) {
			remove(oldc);
		}
	}

	public void add(Package pack) {
		this.metamodel.addPackage(pack);
		packages.add(pack);
		history.add(MergeAction.getAdd(pack));
	}

	public void remove(Package pack) {
		this.metamodel.getContents().remove(pack);
		packages.remove(pack);
		history.add(MergeAction.getDelete(pack));
	}

	public void copy(Package source, Package target, boolean deleteSource) {
		target.getContents().addAll(source.getContents());
		for (ModelElement me : target.getContents()) {
			if (me instanceof Classifier) {
				((Classifier) me).setParent(target);
			}
		}
		history.add(MergeAction.getCopy(EMetamodelSource.OUTPUT, source, EMetamodelSource.OUTPUT, target));
		if (deleteSource)
			remove(source);
	}

	public void add(Classifier clazz) {
		clazz.getParent().getContents().add(clazz);
		classifiers.add(clazz);
		history.add(MergeAction.getAdd(clazz));
	}

	public void remove(Classifier clazz) {
		classifiers.remove(clazz);
		clazz.getParent().getContents().remove(clazz);
		history.add(MergeAction.getDelete(clazz));
	}

	public void copyContents(Class source, Class target, boolean deleteSource) {
		target.getStructuralFeatures().addAll(source.getStructuralFeatures());
		for (StructuralFeature feature : target.getStructuralFeatures()) {
			feature.setParent(target);
		}
		history.add(MergeAction.getCopy(EMetamodelSource.OUTPUT, source, EMetamodelSource.OUTPUT, target));
		if (deleteSource)
			remove(source);
	}

	public void add(StructuralFeature feature) {
		feature.getParent().addStructuralFeature(feature);
		features.add(feature);
		history.add(MergeAction.getAdd(feature));
	}

	public void remove(StructuralFeature feature) {
		features.remove(feature);
		feature.getParent().getStructuralFeatures().remove(feature);
		history.add(MergeAction.getDelete(feature));
	}

	public List<Classifier> getClassifiers() {
		return classifiers;
	}

	public void setClassifiers(List<Classifier> classifiers) {
		this.classifiers = classifiers;
	}

	public List<StructuralFeature> getFeatures() {
		return features;
	}

	public void setFeatures(List<StructuralFeature> features) {
		this.features = features;
	}

	public List<Package> getPackages() {
		return packages;
	}

	public void setPackages(List<Package> packages) {
		this.packages = packages;
	}

	public Metamodel getMetamodel() {
		return metamodel;
	}

	public void setMetamodel(Metamodel metamodel) {
		this.metamodel = metamodel;
	}

	public MergeHistory getHistory() {
		return history;
	}

	public void setHistory(MergeHistory history) {
		this.history = history;
	}

}
